package com.delta.rafg.core.database.impl.util;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import com.delta.rafg.core.database.DataHandlerFactory;
import com.delta.rafg.core.exception.RAFGException;

public class DatabaseUtils {
    /**
    *
    */
	private static final String HEXES = "0123456789ABCDEF";

    /**
     * Mappings of JDBC Types to String Names.
     */
    private static final Map<Integer, String> JDBC_TYPE_NAME_MAP = new HashMap<Integer, String>();

    private static java.util.Random random = new java.util.Random();

    static {
        // Get all field in java.sql.Types
        Field[] fields = java.sql.Types.class.getFields();
        for (int i = 0; i < fields.length; i++) {
            try {
                String name = fields[i].getName();
                Integer value = (Integer) fields[i].get(null);
                JDBC_TYPE_NAME_MAP.put(value, name);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * @param fullyQualifiedTableName
     * @return the table name portion of a fully qualified schema.table reference
     */
    public static String getTableName(String fullyQualifiedTableName) {
        String[] tokens = fullyQualifiedTableName.split("[.](?=([^\"]*\"[^\"]*\")*[^\"]*$)");
        String retStr = null;
        if (tokens != null && tokens.length > 0) {
            retStr = tokens[tokens.length - 1].trim().replace("\"", "");
        }
        return retStr;
    }

    /**
     * @param fullyQualifiedTableName
     * @return the schema name portion of a fully qualified schema.table reference
     */
    public static String getSchemaName(String fullyQualifiedTableName) {
        String[] tokens = fullyQualifiedTableName.split("[.](?=([^\"]*\"[^\"]*\")*[^\"]*$)");
        String retStr = null;
        if (tokens != null && tokens.length > 1) {
            retStr = tokens[0].trim().replace("\"", "");
        }
        return retStr;
    }

    /**
     * Converts a raw byte array to a hexadecimal representation String.
     * @param raw
     * @return String
     */
    public static String getHex(byte [] raw) {
        if (raw == null) {
          return null;
        }
        final StringBuilder hex = new StringBuilder(2 * raw.length);
        for (final byte b : raw) {
          hex.append(HEXES.charAt((b & 0xF0) >> 4))
             .append(HEXES.charAt((b & 0x0F)));
        }
        return hex.toString();
    }

    /**
     * @param resultSet
     * @return
     * @throws SQLException
     */
    public static String dumpRow(ResultSet resultSet) throws SQLException {
        final StringBuilder sb = new StringBuilder();

        ResultSetMetaData meta = resultSet.getMetaData();
        int cols = meta.getColumnCount();
        for (int i = 1; i <= cols; i++) {
            sb.append(meta.getColumnName(i));
            sb.append("[");
            Object o = resultSet.getObject(i);
            if (o instanceof oracle.sql.TIMESTAMP) {
                sb.append(((oracle.sql.TIMESTAMP) o).stringValue());
            } else {
                sb.append(o);
            }
            sb.append("]");
            if (i != cols) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    /**
     * @param jdbcType jdbc type
     * @return type string
     */
    public static String getJdbcTypeName(int jdbcType) {
        return JDBC_TYPE_NAME_MAP.get(jdbcType);
    }
    
    /**
     * Translate column data for placement in bulk import file for Apache Derby
     * bulk insert.
     * @param inputObj
     * @param sqlServerProductName
     * @return
     * @throws SQLException
     * @throws RAFGException
     */
    public static String translateColumnData(Object inputObj, String sqlServerProductName, boolean includeEqualSign, int columnMaxLength, boolean bApplyTestMode) throws SQLException, RAFGException
    {
        if (inputObj == null && includeEqualSign) {
            return " IS NULL";
        }

        if (inputObj == null) {
            return "";
        }
        final StringBuilder retStr = new StringBuilder();
        boolean quotes = true;
        if (includeEqualSign) {
            retStr.append("=");
        }
        if (inputObj instanceof byte[] || inputObj instanceof java.sql.Blob) {
            if (sqlServerProductName.toUpperCase().contains("DERBY")) {
                quotes = false;
            } else if (sqlServerProductName.toUpperCase().contains("MICROSOFT SQL SERVER")) {
                retStr.append("0x");
                quotes = false;
            } else if (sqlServerProductName.toUpperCase().contains("MYSQL")) {
                retStr.append("x");
            } else if (sqlServerProductName.toUpperCase().contains("ORACLE")) {
                retStr.append("X");
            }

            if (quotes) {
                retStr.append("'");
            }
            if (inputObj instanceof byte[]) {
                retStr.append(DatabaseUtils.getHex((byte[]) inputObj));
            } else if (inputObj instanceof java.sql.Blob) {
                java.sql.Blob blob = (java.sql.Blob) inputObj;
                if (blob.length() > Integer.MAX_VALUE) {
                    throw new RAFGException("BLOB data cannot contain more than " + Integer.MAX_VALUE + " bytes of data when generating seed file(s).");
                }
                retStr.append(DatabaseUtils.getHex(blob.getBytes((long) 1, (int) blob.length())));
            } else {
                retStr.append(inputObj);
            }
            if (quotes) {
                retStr.append("'");
            }
//            if (bDerby) {
  //              retStr.append(" AS BLOB)");
//            }
        } else if (inputObj instanceof java.sql.Timestamp || inputObj instanceof java.util.Date) {
            retStr.append("\"");
            retStr.append(inputObj);
            retStr.append("\"");
        } else if (inputObj instanceof String) {
            //need to replace single ' with double '' so that they can be inserted properly.
            String str = ((String) inputObj).replace("\"", "\"\"");
//            String str = (String) inputObj;
            retStr.append("\"");

            // only string data is altered in testmode.
            if (bApplyTestMode) {
                String testStr = Long.toString(Math.abs(random.nextLong()), 36);
                if (testStr.length() > columnMaxLength) {
                    testStr = testStr.substring(0, columnMaxLength);
                }
                retStr.append(testStr);
            } else {
                retStr.append(str);
            }

            retStr.append("\"");
        } else {
            retStr.append(inputObj);
        }
        return retStr.toString();
    }

	public static String getCacheTableName(String schemaName, String productClassTable) throws RAFGException {
		StringBuilder sb = new StringBuilder();
		sb.append(schemaName);
		sb.append(DataHandlerFactory.getCacheDataHandler().getCacheSchemaSuffix());
		sb.append(".");
		sb.append(productClassTable);
		return sb.toString();
	}

}
